/*
 * Callback.c
 *
 *  Created on: 04/giu/2013
 *      Author: Ialenti Andrea, Marangon Fabio
 */

#include "../headers/Callback.h"
#include "../headers/CPLEXProblemGenerator.h"
#include "../headers/CPLEXProblemSolver.h"
#include "../headers/StatusCodes.h"
#include "../headers/Utility.h"
#include "../headers/Printer.h"

#include "../lib/concorde/concorde.h"

bool execMinCutCB(int cnt);

int cbLazySEC(CPXCENVptr env, void *cbdata, int wherefrom, void *cbhandle, int *useraction_p) {
	//	Create a variable used to handle user private data.
	cbData *myData = (cbData*) cbhandle;

	char *funcName = initCharArray(myData->cfg->NAME_LEN);
	sprintf(funcName, "cbLazySEC");

	int i, secCnt, cpxStat;

	if(myData->verbose)
		fprintf(stdout, "\t'%s': running lazy SEC callback #%d. . .\n", funcName, ++myData->cntLS);

	//	Allocate an array of SECs that can store the maximum number of sub-tours for the problem.
	SEC *secs;
	if (!initSECsArray(myData->cfg, &secs, myData->p->nodes / 3, myData->p->nodes)) {
		fprintf(stdout, "***'%s': SECs memory allocation error!\n", funcName);
		freeCharArray(funcName);
		return -1;
	}

	//	Get current node's 'lp'.
	cpxStat = CPXgetcallbacknodelp(env, cbdata, wherefrom, &myData->lp);
	if (cpxStat != CPX_SUCCESS) {
		fprintf(stdout, "***'%s': error retrieving current node's 'lp'!\n", funcName);
		freeCharArray(funcName);
		disposeSECsArray(secs, myData->p->nodes / 3);
		return -1;
	}

	//	Find all SECs at the current node (plot some of them).
	secCnt = findSECs(myData->cfg, (CPXENVptr)env, NULL, NULL, secs, &myData->totSECs, true, myData, cbdata, wherefrom, false);
	if (secCnt < 0) {
		fprintf(stdout, "***'%s': error (%d) searching for sub-tours!\n", funcName, secCnt);
		freeCharArray(funcName);
		disposeSECsArray(secs, myData->p->nodes / 3);
		return -1;
	}

	//	Add all SECs to the problem.
	else if (secCnt > 1)
		for (i = 0; i < secCnt; i++) {
			cpxStat = CPXcutcallbackadd(env, cbdata, wherefrom, secs[i].nonZero,	secs[i].rhs[0], secs[i].sense[0], secs[i].matind, secs[i].matval, CPX_USECUT_FORCE);
			if (cpxStat != CPX_SUCCESS) {
				fprintf(stdout, "***'%s': error adding SEC '%s' to the problem!\n", funcName, secs[i].constrName[0]);
				freeCharArray(funcName);
				disposeSECsArray(secs, myData->p->nodes / 3);
				return -1;
			}
		}

	//	Plot new integer solutions.
	else if (secCnt == 1 && myData->verbose) {

		//	Retrieve the current tour's cost.
		int idx1, idx2, nEdges = myData->numcols;
		myData->x = initDblArray(nEdges);
		cpxStat = CPXgetcallbacknodex(env, cbdata, wherefrom, myData->x, 0, nEdges - 1);
		if (cpxStat != CPX_SUCCESS) {
			fprintf(stdout, "***'%s': error retrieving current tour! CPLEX cpxStat: %d.\n", funcName, cpxStat);
			freeCharArray(funcName);
			disposeSECsArray(secs, myData->p->nodes / 3);
			freeDblArray(myData->x);
			return -1;
		}

		//	Calculate the solution cost.
		double sol = 0;
		for (i = 0; i < nEdges; i++)
			if (myData->x[i] > 1 - myData->cfg->EPS) {
				getEdgeNodes(myData->p, i, &idx1, &idx2);
				sol += myData->g->costs_origin[idx1][idx2];
			}

		if (sol < myData->bestLSSol) {
			myData->bestLSSol = sol;

			//	Plot the current best solution.
			char *file = initCharArray(myData->cfg->NAME_LEN);
			char *title = initCharArray(myData->cfg->NAME_LEN);
			sprintf(file, "%sintegerSol#%d.dat", myData->cfg->PLT_PATH, ++myData->cntLSSol);
			sprintf(title, "CPLEX best known solution #%d. Solution cost: %0.0f.", myData->cntLSSol, sol);
			plotCPLEXSol(myData->cfg, myData->x, myData->p, myData->g, file, title, myData->cfg->COLOR_BLUE);

			//	Free memory.
			freeCharArray(file);
			freeCharArray(title);
		}
		freeDblArray(myData->x);
	}

	if(myData->verbose)
		fprintf(stdout, "\t'%s': %d SECs added to the CPLEX problem. . .\n\n", funcName, secCnt);

	//	Free the memory.
	freeCharArray(funcName);
	disposeSECsArray(secs, myData->p->nodes / 3);
	return 0;
}

int cbMinCut(CPXCENVptr env, void *cbdata, int wherefrom, void *cbhandle, int *useraction_p) {
	//	Create a variable used to handle user private data.
	cbData *myData = (cbData*)cbhandle;

	char *funcName = initCharArray(myData->cfg->NAME_LEN);
	sprintf(funcName, "cbMinCut");

	//	Execute the callback only some times.
	if (++myData->cntMCTot % 1000000 == 0)
			fprintf(stdout, "\t\t'%s': %d calls. . .\n", funcName, myData->cntMCTot);
	if (!execMinCutCB(myData->cntMCTot)) {
		freeCharArray(funcName);
		return 0;
	}
	myData->cntMC++;

	//	Callback private variables.
	int i, cpxStat, q, edgeCnt, idx1, idx2, nComp, currCC, tmpEdge, cutCnt, currentNode = 0, nEdges = myData->numcols;
	double cutVal;
	int *compsCnt = NULL, *comps = NULL, *cut = NULL;
	int *edgesx0x1 = initIntArray(2 * nEdges);
	int *nodes = initIntArray(myData->p->nodes);
	int *edges = initIntArray(nEdges);
	int *tmpNodes = initIntArray(myData->g->size);
	double *x = initDblArray(nEdges);
	double *edgeCosts = initDblArray(nEdges);
	double *vals = initDblArray(nEdges);

	//	Allocate an array of SECs that can store the maximum number of sub-tours for the problem.
	SEC *secs;
	if (!initSECsArray(myData->cfg, &secs, myData->p->nodes / 3, myData->p->nodes)) {
		fprintf(stdout, "***'%s': SECs memory allocation error!\n", funcName);
		freeCharArray(funcName);
		freeDblArray(x);
		freeDblArray(edgeCosts);
		freeDblArray(vals);
		freeIntArray(edgesx0x1);
		freeIntArray(nodes);
		freeIntArray(edges);
		freeIntArray(tmpNodes);
		return -1;
	}

	//	Get the list of edges chosen in the current CPLEX solution.
	cpxStat = CPXgetcallbacknodex(env, cbdata, wherefrom, x, 0, nEdges - 1);
	if (cpxStat != CPX_SUCCESS) {
		fprintf(stdout, "***'%s': error retrieving current node's solution. CPLEX status: %d\n", funcName, cpxStat);
		freeCharArray(funcName);
		freeDblArray(x);
		freeDblArray(edgeCosts);
		freeDblArray(vals);
		freeIntArray(edgesx0x1);
		freeIntArray(nodes);
		freeIntArray(edges);
		freeIntArray(tmpNodes);
		disposeSECsArray(secs, myData->p->nodes / 3);
		return -1;
	}

	//	Retrieve the edges according to the Concorde's data structures.
	for (i = 0, q = 0, edgeCnt = 0; i < nEdges; i++) {
		getEdgeNodes(myData->p, i, &idx1, &idx2);
		edgeCosts[edgeCnt++] = x[i];
		edgesx0x1[q++] = idx1;
		edgesx0x1[q++] = idx2;
	}

	//	Retrieve the connected components.
	CCcut_connect_components(myData->g->size, edgeCnt - 1, edgesx0x1, edgeCosts, &nComp, &compsCnt, &comps);

	//	Fill the 'nodes' array.
	for (currCC = 0; currCC < nComp; currCC++)
		for (i = 0; i < compsCnt[currCC]; i++, currentNode++)
			nodes[comps[currentNode]] = 1;

	//	If there is only one connected component (CC), search the min-cut and add the constraints.
	if (nComp == 1) {
		if(myData->verbose)
			fprintf(stdout, "\t'%s': running min-cut callback #%d (%d), found 1 CC. . .\n", funcName, myData->cntMC, myData->cntMCTot);

		CCcut_mincut(myData->p->nodes, nEdges, edgesx0x1, edgeCosts, &cutVal, &cut, &cutCnt);

		//	Fill the 'tmpNodes' array.
		for (i = 0; i < cutCnt; i++)
			tmpNodes[cut[i]] = 1;

		//	Extract the min-cut edges that are part of the current CPLEX solution.
		for (i = 0, q = 0; i < cutCnt - 1; i += 2) {
			tmpEdge = getEdgeIndex(myData->p, tmpNodes[i], tmpNodes[i + 1]);
			if (tmpEdge > 0)
				if (x[tmpEdge] > 0) {
					edges[q] = tmpEdge;
					vals[q] = myData->p->XeCost[edges[q]];
					q++;
				}
		}

		//	Add the new CPLEX constraint.
		cpxStat = CPXcutcallbackadd(env, cbdata, wherefrom, q, cutVal, 'L', edges, vals, CPX_USECUT_FORCE);
		if (cpxStat != CPX_SUCCESS) {
			fprintf(stdout, "***'%s': error adding current SEC to the problem. CPLEX status: %d!", funcName, cpxStat);

			freeCharArray(funcName);
			freeDblArray(x);
			freeDblArray(edgeCosts);
			freeDblArray(vals);
			freeIntArray(edgesx0x1);
			freeIntArray(nodes);
			freeIntArray(edges);
			freeIntArray(tmpNodes);
			disposeSECsArray(secs, myData->p->nodes / 3);
			freeIntArray(comps);
			freeIntArray(compsCnt);
			freeIntArray(cut);
			return -1;
		}

		if(myData->verbose)
			fprintf(stdout, "\t'%s': constraint added. . .\n\n", funcName);
	}

	//	Free memory.
	freeCharArray(funcName);
	freeDblArray(x);
	freeDblArray(edgeCosts);
	freeDblArray(vals);
	freeIntArray(edgesx0x1);
	freeIntArray(nodes);
	freeIntArray(edges);
	freeIntArray(tmpNodes);
	disposeSECsArray(secs, myData->p->nodes / 3);
	freeIntArray(comps);
	freeIntArray(compsCnt);
	freeIntArray(cut);
	return 0;
}

bool execMinCutCB(int cnt) {

	int r = rand() % 10000;

	if (cnt < 8192)
			return false;
	else if (cnt < 65536) {
		if (r < 24)
			return true;
		else
			return false;
	}
	else if (cnt < 131072) {
		if (r < 16)
			return  true;
		else
			return false;
	}
	else if (cnt < 524288) {
		if (r < 8)
			return  true;
		else
			return false;
	}
	else if (cnt < 2097152) {
		if (r < 6)
			return  true;
		else
			return false;
	}
	else if (cnt < 4194304) {
		if (r < 4)
			return true;
		else
			return false;
	}
	else
		return false;
}
